#include <sstream>

#include "PhysicalDevice.h"

namespace namespace_easy_car_ui
{

PhysicalDevice::PhysicalDevice(VkPhysicalDevice physicalDevice) : 
    m_physicalDevice(physicalDevice)
{
    //https://registry.khronos.org/vulkan/specs/latest/html/vkspec.html#vkGetPhysicalDeviceProperties
    vkGetPhysicalDeviceProperties(m_physicalDevice, &m_physicalDeviceProperties);
    GetQueueFamilyProperties();
    FindGraphicQueueFamily();
    CalcSore();
}

PhysicalDevice::~PhysicalDevice()
{
    m_queueFamilyPropertiesVec.clear();
}

bool PhysicalDevice::IsGraphicAbility()
{
    return m_graphicQueueFamilyIndex.has_value();
}

uint32_t PhysicalDevice::GetScore()
{
    return m_score;
}

VkPhysicalDevice PhysicalDevice::GetHandle()
{
    return m_physicalDevice;
}

uint32_t PhysicalDevice::GetGraphicQueueFamilyIndex()
{
    return m_graphicQueueFamilyIndex.value();
}

std::string PhysicalDevice::GetName()
{
    return std::string{m_physicalDeviceProperties.deviceName};
}

std::string PhysicalDevice::GetSupportedVulkanVersion()
{
    std::stringstream ss;
    ss <<   VK_VERSION_MAJOR(m_physicalDeviceProperties.apiVersion) << "." << 
            VK_VERSION_MINOR(m_physicalDeviceProperties.apiVersion) << "." << 
            VK_VERSION_PATCH(m_physicalDeviceProperties.apiVersion);
    return ss.str();
}

uint32_t PhysicalDevice::FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties)
{
    VkPhysicalDeviceMemoryProperties memProperties;
    vkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &memProperties);

    for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++)
    {
        if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties)
        {
            return i;
        }
    }

    throw std::runtime_error("failed to find suitable memory type!");
}

std::string GetSupportedVulkanVersion();

void PhysicalDevice::GetQueueFamilyProperties()
{
    if (0 == m_queueFamilyPropertiesVec.size())
    {
        uint32_t queueFamilyCount {0};
        //https://registry.khronos.org/vulkan/specs/latest/html/vkspec.html#vkGetPhysicalDeviceQueueFamilyProperties
        vkGetPhysicalDeviceQueueFamilyProperties(m_physicalDevice, &queueFamilyCount, nullptr);

        m_queueFamilyPropertiesVec.resize(queueFamilyCount);
        vkGetPhysicalDeviceQueueFamilyProperties(m_physicalDevice, &queueFamilyCount, m_queueFamilyPropertiesVec.data());
    }
}

void PhysicalDevice::FindGraphicQueueFamily()
{
    for (uint32_t i = 0; i < m_queueFamilyPropertiesVec.size(); ++i)
    {
        if (VK_QUEUE_GRAPHICS_BIT == (m_queueFamilyPropertiesVec[i].queueFlags & VK_QUEUE_GRAPHICS_BIT))
        {
            m_graphicQueueFamilyIndex = i;
            return;
        }
    }

    return;
}

void PhysicalDevice::CalcSore()
{
    if (m_physicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_OTHER)
    {
        m_score += 1000;
    }
    else if (m_physicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU)
    {
        m_score += 4000;
    }
    else if (m_physicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU)
    {
        m_score += 5000;
    }
    else if (m_physicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU)
    {
        m_score += 3000;
    }
    else if (m_physicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU)
    {
        m_score += 2000;
    }
    else
    {
        m_score += 0;
    }
}



} /* namespace namespace_easy_car_ui */